package com.me.flow.service;

import com.alibaba.fastjson.JSON;
import com.me.flow.dto.*;
import com.me.flow.mapper.PendingTaskDao;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.alibaba.fastjson.JSON.toJSONString;

/**
 * 工作流实现逻辑
 */
@Slf4j
public class FlowServiceImpl implements FlowService {
    @Autowired
    private TaskService taskService;

    @Autowired
    private FormService formService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    PendingTaskDao pendingTaskDao;

    private static final int TASK_LIMIT_TIME = 48;
    private static final String COMMENT_TYPE_CLAIM = "taskClaim";
    private static final String COMMENT_TYPE_FORWARD = "taskAssign";
    private static final String TASK_POOL = "public";

    /**
     * 开启一个工作流
     * @param properties
     * @return
     */
    @Override
    public Map<String,Object> startProcessInstance(FlowDto flowDto, Map<String, String> properties) {
        log.info("提交全局表单，开启工作流 表单数据: "+properties);
        String businessKey = flowDto.getBusinessKey();
        String processDefinitionKey = flowDto.getProcessDefinitionKey();
        properties.put("businessKey",businessKey);
        properties.put("processDefinitionKey",processDefinitionKey);
        //流程校验businessKey用于去重
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(businessKey).list();
        if(list.size() > 0){
            log.error("procesInstance repeat!");
            return null;
        }
        //获取流程定义
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
        String processDefinitionId = definition.getId();
        properties.put("processStatus", "running");
        log.info("call activiti service to start a process start.");
        Map map = new HashMap<String, Object>();
        String piid = "";
        try{
            ProcessInstance instance = formService.submitStartFormData(processDefinitionId, businessKey, properties);
            map.put("businessKey", instance.getBusinessKey());
            piid = instance.getProcessInstanceId();
            map.put("processInstanceId", piid);
            map.put("variables", instance.getProcessVariables());
            log.info("call activiti service to start a process success.{}",piid);
        } catch(Exception e){
            log.error("Start process error: " + e);
            return null;
        }
        return map;
    }

    /**
     * 任务认领 从待办任务池里领取任务
     * 2021年4月9日
     * @param request
     * @return
     */
    @Override
    public boolean claimTask(ClaimTaskRequest request) {
        log.info("taskClaim Param:{} ",toJSONString(request));
        String taskId = request.getTaskId();
        try {
            String processInstanceId = request.getProcessInstanId();
            String userName = request.getUserName();
            //根据taskId获取task信息
            HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery().taskId(taskId);
            HistoricTaskInstance task = taskInstanceQuery.list().get(0);
            log.info("activitiTask res:{} " + JSON.toJSONString(task));
            //判断任务有没有被领取
            String owner = task.getOwner();
            if (!StringUtils.isBlank(owner)) {
                log.warn("taskClaim 重复认领任务:{}", taskId);
                return false;
            }
            taskService.setAssignee(taskId,userName);
            taskService.setOwner(taskId,userName);
            //添加任务转派的备注
            String message = userName + "领取任务";
            taskService.addComment(taskId, processInstanceId, COMMENT_TYPE_CLAIM, message);
            return true;
        } catch (Exception e) {
            log.error("taskClaim Exception,taskId:{}|error:{}", taskId, e);
            return false;
        }
    }

    /**
     * 转派任务
     * 2021年4月9日
     * @param request
     * @return
     */
    @Override
    public void reAssignTask(ReAssginTaskRequest request) {
        log.info("taskReAssgin Param:{}", toJSONString(request));
        String taskId = request.getTaskId();
        try {
            String processInstanceId = request.getProcessInstanId();
            String userName = request.getUserName();
            String forwardUser = request.getForwardUser();
            //forward=public 时，流转到工单池
            if(TASK_POOL.equals(forwardUser)){
                //重新回到订单池
                forwardUser = "";
            }
            taskService.setOwner(taskId, forwardUser);
            //添加任务转派的备注
            String message = userName + "转派任务给" + forwardUser;
            taskService.addComment(taskId, processInstanceId, COMMENT_TYPE_FORWARD, message);
        }catch (Exception e) {
            log.error("taskReAssgin Exception,taskId:{}|error:{}", taskId, e);
        }
    }

    /**
     * 暂存任务
     * @param taskDto
     * @param properties
     */
    @Override
    public void saveTask(PendingTaskDto taskDto, Map<String, String> properties) {
        log.info("=====保存任务表单: {}",properties);
        Authentication.setAuthenticatedUserId(taskDto.getUserId());
        String taskId = taskDto.getTaskId();
        String processInstanceId = taskDto.getProcessInstanceId();
        //加入保存的备注
        if(StringUtils.isNotBlank(taskDto.getComment())) {
            taskService.addComment(taskId, processInstanceId, FlowConstant.COMMENT_TYPE_SAVE, taskDto.getComment());
        }
        //保存任务表单内容
        formService.saveFormData(taskId, properties);
    }

    /**
     * 完成任务
     * @param taskDto,properties
     * @return
     */
    @Override
    public void finishTask(PendingTaskDto taskDto,Map<String, String> properties) {
        log.info("=====审批并提交表单: {}",properties);
        Authentication.setAuthenticatedUserId(taskDto.getUserId());
        String taskId = taskDto.getTaskId();
        String processInstanceId = taskDto.getProcessInstanceId();
        //加入审批意见
        if(StringUtils.isNotBlank(taskDto.getOpinion())) {
            taskService.addComment(taskId, processInstanceId, FlowConstant.COMMENT_TYPE_COMPLETE, taskDto.getOpinion());
        }
        //加入审批备注
        if(StringUtils.isNotBlank(taskDto.getComment())) {
            taskService.addComment(taskId, processInstanceId, FlowConstant.COMMENT_TYPE_COMMENT, taskDto.getComment());
        }
        //提交任务审批
        formService.submitTaskFormData(taskId, properties);
    }

    /**
     * 待办任务列表
     * @param paramMap
     * @return
     */
    @Override
    public List<PendingTaskDto> pendingTaskList(Map<String, Object> paramMap) {
        if(paramMap.get("page") !=null){
            paramMap.put("offset",(Integer.valueOf(paramMap.get("page").toString())-1)*Integer.valueOf(paramMap.get("limit").toString()));
        }
        return pendingTaskDao.queryPendingTaskPage(paramMap);
    }

    /**
     * 根据PIID和任务分配人获取待办任务列表
     * @param processInstanceId
     * @param processInstanceBusinessKey
     * @param assignee
     * @return
     */
    @Override
    public List<Task> queryTaskList(String processInstanceId,String processInstanceBusinessKey,String assignee){
        //链式调用
        TaskQuery taskQuery = taskService.createTaskQuery().active();
        if(StringUtils.isNotBlank(processInstanceBusinessKey)) {
            taskQuery = taskQuery.processInstanceBusinessKey(processInstanceBusinessKey);
        }
        if(StringUtils.isNotBlank(processInstanceId)) {
            taskQuery = taskQuery.processInstanceId(processInstanceId);
        }
        if(StringUtils.isNotBlank(assignee)) {
            taskQuery = taskQuery.taskAssignee(assignee);
        }
        return taskQuery.list();
    }

    /**
     * 获取单个任务
     * @param processInstanceId
     * @param processInstanceBusinessKey
     * @param taskId
     * @return
     */
    @Override
    public Task querySingleTask(String processInstanceId,String processInstanceBusinessKey,String taskId) {
        TaskQuery taskQuery = taskService.createTaskQuery().active();
        if (StringUtils.isNotBlank(processInstanceBusinessKey)) {
            taskQuery = taskQuery.processInstanceBusinessKey(processInstanceBusinessKey);
        }
        if (StringUtils.isNotBlank(processInstanceId)) {
            taskQuery = taskQuery.processInstanceId(processInstanceId);
        }
        if (StringUtils.isNotBlank(taskId)) {
            taskQuery = taskQuery.taskId(taskId);
        }
        return taskQuery.singleResult();
    }

    /**
     * 根据piid或者taskId获取业务唯一单据号
     * @param piid
     * @param taskId
     * @return
     */
    @Override
    public String queryBusinessKey(String piid,String taskId){
        ProcessInstance processInstance = null;
        if(StringUtils.isBlank(piid) && StringUtils.isBlank(taskId)){
            return null;
        }
        if (StringUtils.isNotBlank(taskId) && StringUtils.isBlank(piid)) {
            piid = this.querySingleTask(null,null,taskId).getProcessInstanceId();
        }
        //可以直接根据piid得到业务单据号
        if (StringUtils.isNotBlank(piid)) {
            processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(piid).active().singleResult();
        }
        if(processInstance == null){
            return null;
        }
        return processInstance.getBusinessKey();
    }

    /**
     * 获取某个流程实例的操作日志
     * @param piid
     * @return
     */
    @Override
    public List<HistoricTaskInstance> queryTaskHistory(String piid){
        return historyService.createHistoricTaskInstanceQuery().processInstanceId(piid).list();
    }
}
